{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a href=\"https://colab.research.google.com/github/run-llama/llama_index/blob/main/docs/examples/query_engine/RouterQueryEngine.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Using Not Diamond to Select LLMs For Indexes\n",
    "In this tutorial, we demonstrate how to use a router query engine with a selector powered by [Not Diamond](https://www.notdiamond.ai). You can automatically route a query to one of several available LLMs, which will then select the best index for your needs."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.2\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpython -m pip install --upgrade pip\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install -q llama-index-llms-anthropic llama-index-llms-openai"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.2\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpython3.11 -m pip install --upgrade pip\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "!pip install -q llama-index notdiamond"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# NOTE: This is ONLY necessary in jupyter notebook.\n",
    "# Details: Jupyter runs an event-loop behind the scenes.\n",
    "#          This results in nested event-loops when we start an event-loop to make async queries.\n",
    "#          This is normally not allowed, we use nest_asyncio to allow it for convenience.\n",
    "import nest_asyncio\n",
    "\n",
    "nest_asyncio.apply()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!mkdir -p 'data/paul_graham/'\n",
    "!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham/paul_graham_essay.txt'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Routing Queries With Not Diamond"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from typing import List\n",
    "\n",
    "os.environ[\"OPENAI_API_KEY\"] = \"sk-...\"\n",
    "os.environ[\"ANTHROPIC_API_KEY\"] = \"sk-ant-...\"\n",
    "os.environ[\"NOTDIAMOND_API_KEY\"] = \"sk-...\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create Indexes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.core import (\n",
    "    SimpleDirectoryReader,\n",
    "    VectorStoreIndex,\n",
    "    SummaryIndex,\n",
    "    Settings,\n",
    ")\n",
    "from llama_index.core.query_engine import RouterQueryEngine\n",
    "from llama_index.core.tools import QueryEngineTool\n",
    "from llama_index.selectors.notdiamond.base import NotDiamondSelector\n",
    "\n",
    "# load documents\n",
    "documents = SimpleDirectoryReader(\"data/paul_graham\").load_data()\n",
    "nodes = Settings.node_parser.get_nodes_from_documents(documents)\n",
    "\n",
    "# index documents\n",
    "vector_index = VectorStoreIndex.from_documents(documents)\n",
    "summary_index = SummaryIndex.from_documents(documents)\n",
    "query_text = \"What was Paul Graham's role at Yahoo?\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set up Tools for the QueryEngine"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list_query_engine = summary_index.as_query_engine(\n",
    "    response_mode=\"tree_summarize\",\n",
    "    use_async=True,\n",
    ")\n",
    "vector_query_engine = vector_index.as_query_engine()\n",
    "\n",
    "list_tool = QueryEngineTool.from_defaults(\n",
    "    query_engine=list_query_engine,\n",
    "    description=(\n",
    "        \"Useful for summarization questions related to Paul Graham eassy on\"\n",
    "        \" What I Worked On.\"\n",
    "    ),\n",
    ")\n",
    "\n",
    "vector_tool = QueryEngineTool.from_defaults(\n",
    "    query_engine=vector_query_engine,\n",
    "    description=(\n",
    "        \"Useful for retrieving specific context from Paul Graham essay on What\"\n",
    "        \" I Worked On.\"\n",
    "    ),\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create a NotDiamondSelector and RouterQueryEngine"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from notdiamond import NotDiamond\n",
    "\n",
    "client = NotDiamond(\n",
    "    api_key=os.environ[\"NOTDIAMOND_API_KEY\"],\n",
    "    llm_configs=[\"openai/gpt-4o\", \"anthropic/claude-3-5-sonnet-20240620\"],\n",
    ")\n",
    "preference_id = client.create_preference_id()\n",
    "client.preference_id = preference_id\n",
    "\n",
    "nd_selector = NotDiamondSelector(client=client)\n",
    "\n",
    "query_engine = RouterQueryEngine(\n",
    "    selector=nd_selector,\n",
    "    query_engine_tools=[\n",
    "        list_tool,\n",
    "        vector_tool,\n",
    "    ],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Use Not Diamond to Query Indexes\n",
    "\n",
    "Once we've set up our indexes and query engine, we can submit queries as usual."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Paul Graham has contributed to the field of technology and entrepreneurship through his essays and datasets. He has provided a labelled RAG dataset based on one of his essays, which includes queries, reference answers, and reference contexts. Additionally, he has shared insights and code related to using the LlamaIndex RAG pipeline for evaluation purposes.\n"
     ]
    }
   ],
   "source": [
    "response = query_engine.query(\n",
    "    \"Please summarize Paul Graham's working experience.\"\n",
    ")\n",
    "print(str(response))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Paul Graham founded Viaweb after RICS.\n"
     ]
    }
   ],
   "source": [
    "response = query_engine.query(\"What did Paul Graham do after RICS?\")\n",
    "print(str(response))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using NotDiamondSelector as a standalone selector\n",
    "\n",
    "As with LlamaIndex's built-in selectors, you can also use the `NotDiamondSelector` to select an index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "selections=[SingleSelection(index=1, reason=\"The question asks for a summary of a recipe for deviled eggs, which directly aligns with option 2: 'Great for summarizing recipes.' This choice is most relevant as it specifically addresses the task of summarizing recipes, which is exactly what the question is asking for.\")] session_id='078a837f-d8da-4de4-aec9-b44df5ea32ba' llm=LLMConfig(anthropic/claude-3-5-sonnet-20240620)\n"
     ]
    }
   ],
   "source": [
    "from llama_index.core.tools import ToolMetadata\n",
    "from llama_index.selectors.notdiamond.base import NotDiamondSelector\n",
    "\n",
    "from notdiamond import NotDiamond, Metric\n",
    "\n",
    "choices = [\n",
    "    ToolMetadata(\n",
    "        name=\"vector_index\",\n",
    "        description=\"Great for asking questions about recipes.\",\n",
    "    ),\n",
    "    ToolMetadata(\n",
    "        name=\"list_index\", description=\"Great for summarizing recipes.\"\n",
    "    ),\n",
    "]\n",
    "\n",
    "llm_configs = [\"openai/gpt-4o\", \"anthropic/claude-3-5-sonnet-20240620\"]\n",
    "nd_client = NotDiamond(\n",
    "    api_key=os.environ[\"NOTDIAMOND_API_KEY\"],\n",
    "    llm_configs=llm_configs,\n",
    "    preference_id=preference_id,\n",
    ")\n",
    "preference_id = nd_client.create_preference_id()\n",
    "nd_client.preference_id = preference_id\n",
    "nd_selector = NotDiamondSelector(client=nd_client)\n",
    "\n",
    "nd_result = nd_selector.select(\n",
    "    choices, query=\"What is the summary of this recipe for deviled eggs?\"\n",
    ")\n",
    "print(nd_result)\n",
    "\n",
    "# Use the result's session_id to customize your routing logic.\n",
    "metric = Metric(\"accuracy\")\n",
    "score = metric.feedback(\n",
    "    session_id=nd_result.session_id,\n",
    "    llm_config=nd_result.llm,\n",
    "    value=1,\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
